package com.hup.joystick.service.joystick;

import com.hup.joystick.constant.DeviceKey.KeyActionType;
import com.hup.joystick.constant.DeviceKey.KeyType;
import com.hup.joystick.constant.DeviceKey.NativeKeys;
import com.hup.joystick.constant.DeviceKey.StandardKeys;
import com.hup.joystick.model.KeyMapConfig;
import com.hup.joystick.model.joystick.NativeEvent.NativeAction;
import com.hup.joystick.model.joystick.NativeKey;
import com.hup.joystick.model.joystick.StandardEvent;
import com.hup.joystick.model.joystick.StandardEvent.StandardAction;
import com.hup.joystick.model.joystick.StandardKey;
import com.hup.joystick.service.jna.DIJoyState2;
import com.hup.joystick.service.jna.DInputHResultException.HResult;

import java.util.ArrayList;
import java.util.Map.Entry;

/**
 * 设备原始状态,转标准按键事件;
 * @author hugan
 * @date 2020/10/23
 */
public class StandardEventParser {

    /**
     * 当前已按下的按键
     */
    private final ArrayList<StandardAction> lstOldPress = new ArrayList<>();

    public StandardEvent parse(DIJoyState2 state, int hr, KeyMapConfig keyMap) {

        if (!HResult.succeeded(hr)) {
            //查询错误,lstOldPress都改成抬起
            StandardEvent res = new StandardEvent(hr);
            for (StandardAction oldPress : lstOldPress) oldPress.actionType = KeyActionType.TYPE_RELEASED;
            res.actions.addAll(lstOldPress);
            lstOldPress.clear();
            return res;
        }

        StandardEvent event = new StandardEvent(hr);
        if (keyMap == null) return event;
        ArrayList<StandardAction> lstNewPress = stateToActions(state, keyMap);
        ArrayList<StandardAction> lstAlwaysPress = new ArrayList<>();
        //找同时在新旧列表中都按下的按键
        lstNewPress.removeIf(newPress -> {
            for (int i = 0, size = lstOldPress.size(); i < size; i++) {//只删除一个后就return,所以可以正序
                StandardAction oldPress = lstOldPress.get(i);
                if (oldPress.standardKey.key.equals(newPress.standardKey.key)) {
                    if (oldPress.standardKey.type == KeyType.STICK) {
                        //摇杆特殊处理:纳入为[新按下],但数值需要更新
                        oldPress.standardKey.range = newPress.standardKey.range;
                    }
                    //一直都在按下的
                    lstOldPress.remove(i);//从lstOldPress中移除
                    lstAlwaysPress.add(oldPress);//保留旧事件,弃用新的事件对象
                    return true;//从lstNewPress中移除
                }
                //长按事件思路:按键相同时,如果时间间隔大于n,则不过滤事件
            }
            return false;
        });
         /*
        上面处理完后:
        lstAlwaysPress:一直都按下的
        lstNewPress:新按下的
        lstOldPress:新抬起的
         */
        //复用oldPress对象,仅更新类型
        for (StandardAction oldPress : lstOldPress) oldPress.actionType = KeyActionType.TYPE_RELEASED;

        //重构要发送的按键列表[新按下的,新抬起的]
        event.actions.addAll(lstNewPress);
        event.actions.addAll(lstOldPress);

        //重构当前按下的[一直按下的,新按下的]:这里保证了旧按下的一直在前面
        lstOldPress.clear();
        lstOldPress.addAll(lstAlwaysPress);
        lstOldPress.addAll(lstNewPress);
        //克隆放入到event中
        event.currentPress.addAll(lstOldPress);
        return event;
    }

    private ArrayList<StandardAction> stateToActions(DIJoyState2 s, KeyMapConfig keyMap) {
        ArrayList<StandardAction> res = new ArrayList<>();

        //得到原始事件:[摇杆和按钮(LR2)]
        ArrayList<NativeAction> lstNativeAction = new ArrayList<>();
        NativeEventParser.stickToKey(lstNativeAction, s.lX, NativeKeys.X);
        NativeEventParser.stickToKey(lstNativeAction, s.lY, NativeKeys.Y);
        NativeEventParser.stickToKey(lstNativeAction, s.lZ, NativeKeys.Z);
        NativeEventParser.stickToKey(lstNativeAction, s.lRx, NativeKeys.RX);
        NativeEventParser.stickToKey(lstNativeAction, s.lRy, NativeKeys.RY);
        NativeEventParser.stickToKey(lstNativeAction, s.lRz, NativeKeys.RZ);
        NativeEventParser.arrayToKey(lstNativeAction, s.rglSlider, NativeKeys.SLIDER); //未知字段
        NativeEventParser.stickToKey(lstNativeAction, s.lVX, NativeKeys.VX);
        NativeEventParser.stickToKey(lstNativeAction, s.lVY, NativeKeys.VY);
        NativeEventParser.stickToKey(lstNativeAction, s.lVZ, NativeKeys.VZ);
        NativeEventParser.stickToKey(lstNativeAction, s.lVRx, NativeKeys.VRX);
        NativeEventParser.stickToKey(lstNativeAction, s.lVRy, NativeKeys.VRY);
        NativeEventParser.stickToKey(lstNativeAction, s.lVRz, NativeKeys.VRZ);
        NativeEventParser.arrayToKey(lstNativeAction, s.rglVSlider, NativeKeys.V_SLIDER);
        NativeEventParser.stickToKey(lstNativeAction, s.lAX, NativeKeys.AX);
        NativeEventParser.stickToKey(lstNativeAction, s.lAY, NativeKeys.AY);
        NativeEventParser.stickToKey(lstNativeAction, s.lAZ, NativeKeys.AZ);
        NativeEventParser.stickToKey(lstNativeAction, s.lARx, NativeKeys.ARX);
        NativeEventParser.stickToKey(lstNativeAction, s.lARy, NativeKeys.ARY);
        NativeEventParser.stickToKey(lstNativeAction, s.lARz, NativeKeys.ARZ);
        NativeEventParser.arrayToKey(lstNativeAction, s.rglASlider, NativeKeys.A_SLIDER);
        NativeEventParser.stickToKey(lstNativeAction, s.lFX, NativeKeys.FX);
        NativeEventParser.stickToKey(lstNativeAction, s.lFY, NativeKeys.FY);
        NativeEventParser.stickToKey(lstNativeAction, s.lFZ, NativeKeys.FZ);
        NativeEventParser.stickToKey(lstNativeAction, s.lFRx, NativeKeys.FRX);
        NativeEventParser.stickToKey(lstNativeAction, s.lFRy, NativeKeys.FRY);
        NativeEventParser.stickToKey(lstNativeAction, s.lFRz, NativeKeys.FRZ);
        NativeEventParser.arrayToKey(lstNativeAction, s.rglFSlider, NativeKeys.F_SLIDER);
        for (int i = 0; i < s.rgbButtons.length; i++) {
            if ((s.rgbButtons[i] & 0x80) != 0)
                lstNativeAction.add(new NativeAction(NativeKey.button(NativeKeys.getButtonKey(i), i), KeyActionType.TYPE_PRESSED));
        }

        //构造标准[摇杆,按钮]
        for (NativeAction nativeAction : lstNativeAction) {//遍历事件,而不是遍历配置,可以减少运算量
            String nativeKey = nativeAction.nativeKey.key;
            long nativeValue = nativeAction.nativeKey.nativeValue;
            //映射到[标准摇杆]
            StandardKey standardStick = null;
            if (keyMap.stickLX != null && nativeKey.startsWith(keyMap.stickLX)) {
                standardStick = StandardKeys.getStick(true, true, nativeValue);
            } else  if (keyMap.stickLY != null && nativeKey.startsWith(keyMap.stickLY)) {
                standardStick = StandardKeys.getStick(true, false, nativeValue);
            } else  if (keyMap.stickRX != null && nativeKey.startsWith(keyMap.stickRX)) {
                standardStick = StandardKeys.getStick(false, true, nativeValue);
            } else  if (keyMap.stickRY != null && nativeKey.startsWith(keyMap.stickRY)) {
                standardStick = StandardKeys.getStick(false, false, nativeValue);
            }
            if (standardStick != null) {
                res.add(new StandardAction(standardStick, KeyActionType.TYPE_PRESSED));
                continue;
            }

            //非标准摇杆,映射到[标准按钮]
            for (Entry<String, String> entry : keyMap.buttonMap.entrySet()) {
                String nativeKeyStr = entry.getValue();
                if (nativeKey.equals(nativeKeyStr)) {
                    String standardButtonKey = entry.getKey();
                    res.add(new StandardAction(StandardKey.button(standardButtonKey), KeyActionType.TYPE_PRESSED));
                    break;
                }
            }
        }

        //方向键
        if (keyMap.arrowIndex != KeyMapConfig.NULL_ARROW_INDEX
                && s.rgdwPOV[keyMap.arrowIndex] != NativeKeys.ARROW_DEF_VALUE) {
            StandardKey standardArrow = StandardKeys.getArrow(s.rgdwPOV[keyMap.arrowIndex]);
            if (standardArrow != null) {
                res.add(new StandardAction(standardArrow, KeyActionType.TYPE_PRESSED));
            }
        }
        return res;
    }

}
